package gui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.TitledBorder;
import javax.swing.table.TableModel;

import runner.ExperimentsRunner;

import com.jgoodies.forms.factories.FormFactory;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;

import core.algorithms.BasicHillClimbing;
import core.algorithms.FirstChoiceHCWithRandomRestart;
import core.algorithms.RandomWalkHillClimbing;
import core.algorithms.SearchParameters;
import core.course.TimeTable;
import core.experiment.Experiment;
import core.experiment.Experiment.SearchParamToControl;
import core.heuristic.HeuristicsManager;
import core.interfaces.IExperimentListener;
import core.interfaces.IProgressListener;
import core.interfaces.ISearchAlgorithm;
import core.interfaces.IState;
import core.interfaces.IUtility;

public class AlgorithmRunnerGUI implements IProgressListener, IExperimentListener
{

	private JCheckBox m_BindDrawsToCourses;
	private JTextField m_DrawCoursesPercentage;
	private JCheckBox m_BoundHeuristicCalss;
	private JList m_HeuristicList;
	private JTextField m_Incerement;
	private JTextField m_IncrementIterations;
	private JComboBox m_VarToControl;
	private JProgressBar m_CurrentSearchsProgressBar;
	private JTextField m_ExperimentsNum;
	private JTextField m_DrawLimit;
	private JTextField m_RandomSteps;
	private JProgressBar m_ExperimentsProgressBar;
	private JTextField m_SpaceSeed;
	private JTextField m_MaxHeuristicCalls;
	private JRadioButton m_ExpTypeFirstChoiceHC;
	private JRadioButton m_ExpTypeRandomWalkHC;
	private JTextField m_CourseNumber;
	private JRadioButton m_ExpTypeBasicHC;
	private ButtonGroup m_ExperimentTypeButtonGroup = new ButtonGroup();
	private JTable m_experimentsTable;
	private JButton m_startButton;
	private JFrame m_Frame;

	/**
	 * Launch the application
	 * @param args
	 */
	public static void main(String args[])
	{
		HeuristicsManager.STATIC_INIT();
		EventQueue.invokeLater(new Runnable()
		{
			public void run()
			{
				try
				{
					AlgorithmRunnerGUI window = new AlgorithmRunnerGUI();
					window.m_Frame.setVisible(true);
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application
	 */
	public AlgorithmRunnerGUI()
	{
		createContents();
	}

	/**
	 * Initialize the contents of the frame
	 */
	private void createContents()
	{
		try {
		      UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
		    } catch (Exception e) {
		      e.printStackTrace();
		    }
		m_Frame = new JFrame();
	//	m_Frame.setSize(800, 600);
		m_Frame.setPreferredSize(new Dimension(1024, 768));
		m_Frame.setTitle("Experiments Runner");
		m_Frame.getContentPane().setLayout(new BorderLayout());
		m_Frame.setExtendedState(m_Frame.getExtendedState() | java.awt.Frame.MAXIMIZED_BOTH);
		//m_Frame.setBounds(100, 100, 692, 584);
		m_Frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		final JPanel panel_1 = new JPanel();
		panel_1.setLayout(new FormLayout(
			new ColumnSpec[] {
				FormFactory.RELATED_GAP_COLSPEC,
				ColumnSpec.decode("default:grow(1.0)"),
				FormFactory.RELATED_GAP_COLSPEC},
			new RowSpec[] {
				FormFactory.RELATED_GAP_ROWSPEC,
				RowSpec.decode("fill:default:grow(1.0)"),
				FormFactory.RELATED_GAP_ROWSPEC,
				RowSpec.decode("fill:240px"),
				FormFactory.RELATED_GAP_ROWSPEC,
				RowSpec.decode("fill:40dlu:grow(1.0)"),
				FormFactory.RELATED_GAP_ROWSPEC,
				RowSpec.decode("fill:40dlu")}));
		m_Frame.getContentPane().add(panel_1);

		final JPanel panel = new JPanel();
		panel_1.add(panel, new CellConstraints(2, 6));
		panel.setLayout(new FormLayout(
			new ColumnSpec[] {
				FormFactory.RELATED_GAP_COLSPEC,
				ColumnSpec.decode("default:grow(1.0)"),
				FormFactory.RELATED_GAP_COLSPEC},
			new RowSpec[] {
				FormFactory.RELATED_GAP_ROWSPEC,
				RowSpec.decode("fill:default:grow(1.0)"),
				FormFactory.RELATED_GAP_ROWSPEC}));
		panel.setBorder(new TitledBorder(null, "Total Progress", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null));

		m_ExperimentsProgressBar = new JProgressBar();
		m_ExperimentsProgressBar.setString("Total");
		panel.add(m_ExperimentsProgressBar, new CellConstraints(2, 2));

		final JPanel newExperimentPanel = new JPanel();
		newExperimentPanel.setMinimumSize(new Dimension(0, 0));
		newExperimentPanel.setPreferredSize(new Dimension(0, 550));
		panel_1.add(newExperimentPanel, new CellConstraints(2, 4));
		newExperimentPanel.setLayout(null);
		newExperimentPanel.setBorder(new TitledBorder(null, "New Experiment", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null));

		m_ExpTypeBasicHC = new JRadioButton();
		m_ExperimentTypeButtonGroup.add(m_ExpTypeBasicHC);
		m_ExpTypeBasicHC.setText("Basic Hill Climbing");
		m_ExpTypeBasicHC.setBounds(10, 31, 200, 25);
		newExperimentPanel.add(m_ExpTypeBasicHC);

		m_ExpTypeRandomWalkHC = new JRadioButton();
		m_ExperimentTypeButtonGroup.add(m_ExpTypeRandomWalkHC);
		m_ExpTypeRandomWalkHC.setText("Random Walk HC With SBS");
		m_ExpTypeRandomWalkHC.setBounds(10, 62, 217, 25);
		newExperimentPanel.add(m_ExpTypeRandomWalkHC);

		m_ExpTypeFirstChoiceHC = new JRadioButton();
		m_ExperimentTypeButtonGroup.add(m_ExpTypeFirstChoiceHC);
		m_ExpTypeFirstChoiceHC.setText("First Choice HC with RR and SBS");
		m_ExpTypeFirstChoiceHC.setBounds(10, 93, 217, 25);
		newExperimentPanel.add(m_ExpTypeFirstChoiceHC);

		m_CourseNumber = new JTextField();
		m_CourseNumber.setText("250");
		m_CourseNumber.setBounds(365, 33, 82, 21);
		newExperimentPanel.add(m_CourseNumber);

		final JLabel courseNumberLabel = new JLabel();
		courseNumberLabel.setText("Courses Number:");
		courseNumberLabel.setBounds(249, 35, 110, 17);
		newExperimentPanel.add(courseNumberLabel);

		final JButton m_addExperimentButton = new JButton();
		m_addExperimentButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0)
			{
				doAddExperiment();
			}
		});
		m_addExperimentButton.setText("Add");
		m_addExperimentButton.setBounds(850, 198, 106, 21);
		newExperimentPanel.add(m_addExperimentButton);

		final JLabel courseNumberLabel_1_1 = new JLabel();
		courseNumberLabel_1_1.setText("Max Steps/H. calls:");
		courseNumberLabel_1_1.setBounds(249, 66, 123, 17);
		newExperimentPanel.add(courseNumberLabel_1_1);

		m_MaxHeuristicCalls = new JTextField();
		m_MaxHeuristicCalls.setText("2500");
		m_MaxHeuristicCalls.setBounds(365, 64, 82, 21);
		newExperimentPanel.add(m_MaxHeuristicCalls);

		final JLabel courseNumberLabel_2 = new JLabel();
		courseNumberLabel_2.setText("Initial Seed:");
		courseNumberLabel_2.setBounds(498, 66, 110, 17);
		newExperimentPanel.add(courseNumberLabel_2);

		m_SpaceSeed = new JTextField();
		m_SpaceSeed.setText("1000");
		m_SpaceSeed.setBounds(627, 64, 100, 21);
		newExperimentPanel.add(m_SpaceSeed);

		final JLabel courseNumberLabel_3 = new JLabel();
		courseNumberLabel_3.setText("Random Steps:");
		courseNumberLabel_3.setBounds(249, 131, 110, 17);
		newExperimentPanel.add(courseNumberLabel_3);

		m_RandomSteps = new JTextField();
		m_RandomSteps.setText("50");
		m_RandomSteps.setBounds(365, 129, 100, 21);
		newExperimentPanel.add(m_RandomSteps);

		final JLabel courseNumberLabel_3_1 = new JLabel();
		courseNumberLabel_3_1.setText("Draw limit:");
		courseNumberLabel_3_1.setBounds(249, 158, 110, 17);
		newExperimentPanel.add(courseNumberLabel_3_1);

		m_DrawLimit = new JTextField();
		m_DrawLimit.setText("50");
		m_DrawLimit.setBounds(365, 156, 100, 21);
		newExperimentPanel.add(m_DrawLimit);

		final JLabel courseNumberLabel_2_1 = new JLabel();
		courseNumberLabel_2_1.setText("Experiments:");
		courseNumberLabel_2_1.setBounds(498, 35, 110, 17);
		newExperimentPanel.add(courseNumberLabel_2_1);

		m_ExperimentsNum = new JTextField();
		m_ExperimentsNum.setText("10");
		m_ExperimentsNum.setBounds(627, 33, 100, 21);
		newExperimentPanel.add(m_ExperimentsNum);

		final JLabel courseNumberLabel_1_2 = new JLabel();
		courseNumberLabel_1_2.setText("Variable to control");
		courseNumberLabel_1_2.setBounds(498, 97, 110, 17);
		newExperimentPanel.add(courseNumberLabel_1_2);

		m_VarToControl = new JComboBox();
		m_VarToControl.setBounds(627, 93, 100, 21);
		newExperimentPanel.add(m_VarToControl);
		addItemsToControlVarComboBox();
		
		final JLabel courseNumberLabel_4 = new JLabel();
		courseNumberLabel_4.setText("Inc. Iterations");
		courseNumberLabel_4.setBounds(498, 131, 123, 17);
		newExperimentPanel.add(courseNumberLabel_4);

		m_IncrementIterations = new JTextField();
		m_IncrementIterations.setToolTipText("this is the number of lines that will be created");
		m_IncrementIterations.setText("2");
		m_IncrementIterations.setBounds(627, 129, 100, 21);
		newExperimentPanel.add(m_IncrementIterations);

		final JLabel courseNumberLabel_4_1 = new JLabel();
		courseNumberLabel_4_1.setText("Increment:");
		courseNumberLabel_4_1.setBounds(498, 158, 123, 17);
		newExperimentPanel.add(courseNumberLabel_4_1);

		m_Incerement = new JTextField();
		m_Incerement.setText("1");
		m_Incerement.setBounds(627, 156, 100, 21);
		newExperimentPanel.add(m_Incerement);

		m_HeuristicList = new JList();
		m_HeuristicList.setBounds(749, 55, 207, 63);
		m_HeuristicList.setModel(HeuristicsManager.getInstance());
		newExperimentPanel.add(m_HeuristicList);

		final JLabel courseNumberLabel_1 = new JLabel();
		courseNumberLabel_1.setText("Heuristics:");
		courseNumberLabel_1.setBounds(749, 26, 110, 17);
		newExperimentPanel.add(courseNumberLabel_1);

		m_BoundHeuristicCalss = new JCheckBox();
		m_BoundHeuristicCalss.setText("Bound H. Calls");
		m_BoundHeuristicCalss.setBounds(249, 93, 123, 25);
		newExperimentPanel.add(m_BoundHeuristicCalss);

		m_BindDrawsToCourses = new JCheckBox();
		m_BindDrawsToCourses.setText("Bind draws to courses");
		m_BindDrawsToCourses.setBounds(749, 127, 207, 25);
		newExperimentPanel.add(m_BindDrawsToCourses);

		m_DrawCoursesPercentage = new JTextField();
		m_DrawCoursesPercentage.setToolTipText("The experiment will keep the number of draws = thisValue x 0.01 x courses Number");
		m_DrawCoursesPercentage.setText("40");
		m_DrawCoursesPercentage.setBounds(913, 156, 43, 21);
		newExperimentPanel.add(m_DrawCoursesPercentage);

		final JLabel courseNumberLabel_4_1_1 = new JLabel();
		courseNumberLabel_4_1_1.setText("Percentage 0.0-100.0 %:");
		courseNumberLabel_4_1_1.setBounds(749, 158, 158, 17);
		newExperimentPanel.add(courseNumberLabel_4_1_1);

		final JPanel topGroupPanel = new JPanel();
		panel_1.add(topGroupPanel, new CellConstraints(2, 2));
		topGroupPanel.setLayout(new FormLayout(
			new ColumnSpec[] {
				FormFactory.RELATED_GAP_COLSPEC,
				ColumnSpec.decode("default:grow(1.0)"),
				FormFactory.RELATED_GAP_COLSPEC},
			new RowSpec[] {
				FormFactory.RELATED_GAP_ROWSPEC,
				RowSpec.decode("fill:default:grow(1.0)"),
				FormFactory.RELATED_GAP_ROWSPEC}));
		topGroupPanel.setBorder(new TitledBorder(null, "Experiments", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null));

		final JScrollPane scrollPane = new JScrollPane();
		topGroupPanel.add(scrollPane, new CellConstraints(2, 2, CellConstraints.FILL, CellConstraints.DEFAULT));

		m_experimentsTable = new JTable();
		m_experimentsTable.addMouseListener(new MouseAdapter() {
			public void mouseClicked(final MouseEvent arg0)
			{
				// getting specific tables is disabled
				if (true)
				{
					return;
				}
				int rowAtPoint = m_experimentsTable.rowAtPoint(arg0.getPoint());
				if (rowAtPoint > -1)
				{
					TableModel tableModel = m_experimentsTable.getModel();
					if (tableModel instanceof ExperimentsContainer)
					{
						try
						{
							ExperimentsContainer experimentsContainer = (ExperimentsContainer) tableModel;
							TimeTable timeTable = experimentsContainer.getTimeTable(rowAtPoint);
							TimeTableDialog timeTableDialog = new TimeTableDialog(timeTable);
							timeTableDialog.setVisible(true);
							
						}
						catch (Exception e)
						{
							
						}
			
					}
		
				}
			}
		});
		scrollPane.setViewportView(m_experimentsTable);
		m_experimentsTable.setShowGrid(true);
		m_experimentsTable.setModel(new ExperimentsContainer());

		final JPanel panel_2 = new JPanel();
		panel_2.setBorder(new TitledBorder(null, "Current Search Algorithm Progress", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null));
		panel_2.setLayout(new FormLayout(
			new ColumnSpec[] {
				FormFactory.RELATED_GAP_COLSPEC,
				ColumnSpec.decode("default:grow(1.0)"),
				FormFactory.RELATED_GAP_COLSPEC},
			new RowSpec[] {
				FormFactory.RELATED_GAP_ROWSPEC,
				RowSpec.decode("fill:default:grow(1.0)"),
				FormFactory.RELATED_GAP_ROWSPEC}));
		panel_1.add(panel_2, new CellConstraints(2, 8));

		m_CurrentSearchsProgressBar = new JProgressBar();
		m_CurrentSearchsProgressBar.setString("Total");
		panel_2.add(m_CurrentSearchsProgressBar, new CellConstraints(2, 2));

		final JPanel panel_3 = new JPanel();
		panel_3.setLayout(new GridLayout(1, 0));
		panel_3.setPreferredSize(new Dimension(0, 50));
		final FlowLayout flowLayout = new FlowLayout();
		flowLayout.setAlignment(FlowLayout.RIGHT);
		panel_3.setLayout(flowLayout);
		m_Frame.getContentPane().add(panel_3, BorderLayout.SOUTH);
		
		m_startButton = new JButton();
		m_startButton.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0)
			{
				doStartExperiments();
			}
		});
		panel_3.add(m_startButton);
		m_startButton.setText("start");

		final JMenuBar menuBar = new JMenuBar();
		m_Frame.setJMenuBar(menuBar);

		final JMenu fileMenu = new JMenu();
		fileMenu.setText("File");
		menuBar.add(fileMenu);

		final JMenu tableMenu = new JMenu();
		tableMenu.setText("Table");
		menuBar.add(tableMenu);

		final JMenuItem m_ClearTableMenuItem = new JMenuItem();
		m_ClearTableMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(final ActionEvent arg0)
			{
				TableModel tableModel = m_experimentsTable.getModel();
				if (tableModel instanceof ExperimentsContainer)
				{
					ExperimentsContainer experimentsContainer = (ExperimentsContainer) tableModel;
					experimentsContainer.clear();
				}
			}
		});
		m_ClearTableMenuItem.setText("Clear");
		tableMenu.add(m_ClearTableMenuItem);

	}


	protected void doStartExperiments()
	{
		TableModel tableModel = m_experimentsTable.getModel();
		m_ExperimentsProgressBar.setValue(0);
		m_startButton.setEnabled(false);
		if (tableModel instanceof ExperimentsContainer)
		{
			ExperimentsContainer experimentsContainer = (ExperimentsContainer) tableModel;
			final ExperimentsRunner<TimeTable> experimentsRunner = 
				new ExperimentsRunner<TimeTable>(
						experimentsContainer.getSearchAlgorithmList(), this, this);
			Runnable expermintesRunnable = new Runnable()
			{
				@Override
				public void run()
				{
					experimentsRunner.runExperiments();
					onExperimentComplete(experimentsRunner);
				}
			};
			Thread thread = new Thread(expermintesRunnable, "ExperimentsThread");
			thread.start();
		}		
		
	}

	protected void onExperimentComplete(final ExperimentsRunner<TimeTable> experimentsRunner)
	{
		Runnable updateTable = new Runnable()
		{
			@Override
			public void run()
			{
				m_startButton.setEnabled(true);
				m_ExperimentsProgressBar.setValue(100);
				TableModel tableModel = m_experimentsTable.getModel();
				if (tableModel instanceof ExperimentsContainer)
				{
					ExperimentsContainer experimentsContainer = (ExperimentsContainer) tableModel;
					experimentsContainer.updateData();
				}
				
			}
		};
		
		SwingUtilities.invokeLater(updateTable);
	}

	private void addItemsToControlVarComboBox()
	{
		SearchParamToControl[] values = Experiment.SearchParamToControl.values();
		for (SearchParamToControl searchParamToControl : values)
		{
			m_VarToControl.addItem(searchParamToControl);
		}
	}
	
	@SuppressWarnings("unchecked")
	protected void doAddExperiment()
	{
		ISearchAlgorithm<TimeTable> searchAlgorithm = null;
		try
		{
			// get values from the GUI
			int spaceSeed = Integer.parseInt(m_SpaceSeed.getText());
			int randomSteps = Integer.parseInt(m_RandomSteps.getText());
			int drawLimit = Integer.parseInt(m_DrawLimit.getText());
			int heuristicCalls = Integer.parseInt(m_MaxHeuristicCalls.getText());
			int numCourses = Integer.parseInt(m_CourseNumber.getText());
			boolean m_BoundHeursticCalls = m_BoundHeuristicCalss.isSelected();
			IUtility<IState<TimeTable>, Double> heuristic = (IUtility<IState<TimeTable>, Double>) m_HeuristicList.getSelectedValue();
			int numExperiments = Integer.parseInt(m_ExperimentsNum.getText());
			Experiment.SearchParamToControl searchParamToControl = (SearchParamToControl) m_VarToControl.getSelectedItem();
			int incrementIterations = Integer.parseInt(m_IncrementIterations.getText());
			int incrementStep = Integer.parseInt(m_Incerement.getText());
			boolean bindDrawsCourses = m_BindDrawsToCourses.isSelected();
			double drawCoursesPercentage = Double.parseDouble(m_DrawCoursesPercentage.getText());
			
			if (heuristic != null && incrementStep != 0)
			{
				// create search parameters
				SearchParameters<TimeTable> searchParameters = new SearchParameters<TimeTable>();
				searchParameters.setHeuristic(heuristic );
				searchParameters.setMaximumHeuristicCalls(heuristicCalls);
				searchParameters.setSpaceSeed(spaceSeed);
				searchParameters.setBoundHeuristicCalls(m_BoundHeursticCalls);
				searchParameters.setRandomSteps(randomSteps);
				searchParameters.setDrawLimit(drawLimit);
				searchParameters.setCourseNumber(numCourses);
				
				if (m_ExpTypeBasicHC.isSelected())
				{
					// create search object
					searchAlgorithm = new BasicHillClimbing<TimeTable>(searchParameters);
				}
				else if (m_ExpTypeRandomWalkHC.isSelected())
				{
					// create search object
					searchAlgorithm = new RandomWalkHillClimbing<TimeTable>(searchParameters);
				}
				else if (m_ExpTypeFirstChoiceHC.isSelected())
				{
					// create search object
					searchAlgorithm = new FirstChoiceHCWithRandomRestart<TimeTable>(searchParameters);				
				}
				
				
				if (searchAlgorithm != null)
				{
					TableModel tableModel = m_experimentsTable.getModel();
					if (tableModel instanceof ExperimentsContainer)
					{
						ExperimentsContainer experimentsContainer = (ExperimentsContainer) tableModel;
						
						searchAlgorithm.addProgressListener(this);
						
						// create an experiment from the search object
						List<Experiment> experiments = Experiment.getExperiments(
								searchAlgorithm,
								spaceSeed,
								numExperiments,
								searchParamToControl,
								incrementIterations,
								incrementStep,
								Experiment.getUniqueId(),
								bindDrawsCourses,
								drawCoursesPercentage);
						experimentsContainer.add(experiments);
				
					}
				}	

			}
	
		}
		catch (Exception e)
		{
			JOptionPane.showMessageDialog(this.m_Frame, e.getMessage(), "error", JOptionPane.ERROR_MESSAGE);
		}

		
	}

	@Override
	public void percentageComplete(double percentage, final int type)
	{
		final int	value = (int) Math.round(percentage);
		// System.out.println(value);
		Runnable runnable = new Runnable()
		{
			@Override
			public void run()
			{
				switch (type)
				{
				case 1: 
					m_ExperimentsProgressBar.setValue(value);
					break;
				case 2:
					m_CurrentSearchsProgressBar.setValue(value);
					break;
				}
				


			}
		};
		SwingUtilities.invokeLater(runnable);
		
	}

	@Override
	public void experimentComplete()
	{
		TableModel tableModel = m_experimentsTable.getModel();
		if (tableModel instanceof ExperimentsContainer)
		{
			ExperimentsContainer experimentsContainer = (ExperimentsContainer) tableModel;
			experimentsContainer.updateData();
	
		}
		
	}




	
}
